#include <ani.h>
#include <array>
#include <iostream>

static void NativeFunc(ani_env *env, ani_object obj)
{
    ani_method managedMethod;
    ani_type result;
    
    static const char *className = "ani_call_method.Foo";
    ani_class cls;
    if (ANI_OK != env->FindClass(className, &cls)) {
        std::cerr << "Not found '" << className << "'" << std::endl;
        return ;
    }
    if(ANI_OK != env->Class_FindMethod(cls, "ManagedFunc", ":", &managedMethod)){
        std::cerr << "Class_FindMethod Faild" << std::endl;
        return ;
    }
    std::cout << "Print in Native Func" << std::endl;
    env->Object_CallMethod_Void(obj, managedMethod);
}

void BarConstructor(ani_env *env, ani_object obj, ani_object stringObj){
    env->Object_SetFieldByName_Ref(obj, "name", stringObj);
}

ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result)
{
    ani_env *env;
    if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) {
        std::cerr << "Unsupported ANI_VERSION_1" << std::endl;
        return ANI_ERROR;
    }

    static const char *className = "ani_call_method.Foo";
    ani_class cls;
    if (ANI_OK != env->FindClass(className, &cls)) {
        std::cerr << "Not found '" << className << "'" << std::endl;
        return ANI_ERROR;
    }

    std::array methods = {
        ani_native_function {"NativeFunc", ":", reinterpret_cast<void *>(NativeFunc)},
    };

    if (ANI_OK != env->Class_BindNativeMethods(cls, methods.data(), methods.size())) {
        std::cerr << "Cannot bind native methods to '" << className << "'" << std::endl;
        return ANI_ERROR;
    };


    ani_class barCls;
    static const char *barClassName = "ani_call_method.Bar";

    if (ANI_OK != env->FindClass(barClassName, &barCls)) {
        std::cerr << "Not found '" << barClassName << "'" << std::endl;
        return ANI_ERROR;
    }

    std::array barMethods = {
        ani_native_function {"<ctor>", "C{std.core.String}:", reinterpret_cast<void *>(BarConstructor)},
    };

    if (ANI_OK != env->Class_BindNativeMethods(barCls, barMethods.data(), barMethods.size())) {
        std::cerr << "Cannot bind native methods to '" << barClassName << "'" << std::endl;
        return ANI_ERROR;
    };

    *result = ANI_VERSION_1;
    return ANI_OK;
}